/*
  DyBar - Dynamic Toolbar for interactive Web Applications
   
  File:    dybar.js
  Purpose: A dynamic toolbar which can be added to any webpage in order to
           provide interactive tools.

  Usage:   Place this file next to your webpage and put
           <script type="text/javascript" src="dybar.js"></script>
           *after* the <body> section.
           To add actual tools you need to call addTool() with the respective
           parameter. Please consult the documentation for details.
 
  Copyright 2007-2008 Christian Kofler <christian.kofler@googlemail.com>

  You may not use this file except under the terms of the accompanying license.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License. You may
  obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

//------------------------------------------------------------------------------
// globals
//------------------------------------------------------------------------------

document.onLoad = init();
var isFirefox = document.getElementById && !document.all;
var dybarDiv;
var dialogsDiv;


//------------------------------------------------------------------------------
// public functions / DyBar API
//------------------------------------------------------------------------------

/**
 * Add the HTML code for a DyBar tool to the actual DyBar.
 * The tool to add should usually be generated with one the available
 * create functions.
 *
 * @param tool HTML code of a DyBar tool
 */
function addTool(tool) {
    dybarDiv.innerHTML += tool;
}

/**
 * Creates HTML for a button element which can be clicked.
 * The label of the button can be either plain text, i.e. it will look like
 * a typical button, or it can be html code which allows for complex design.
 * A callback function is expected which will be executed when the button is clicked.
 * The callback will receive no parameter.
 *
 * @param label    the label of the toggle (plain text or html)
 * @param callback the function to be called on change of the toggle's state
 *
 * @return HTML code for the newly created button element
 */ 
function createButton(label, callback) {
    if(!callback) callback = 0;
    retval = '<span class="button" title="'+ label +'" ';
    retval += ' onClick="javascript:button(this, '+ callback +')">'+ label +"</span>";
    return retval;
}

/**
 * Creates HTML for a toggle element which can be either enabled or disabled.
 * The label of the toggle can be either plain text, i.e. it will look like
 * a typical toggle button, or it can be html code which allows for complex design.
 * A callback function is expected which will be executed when the toggle is clicked.
 * The callback will receive the current state of the toggle as boolean parameter, i.e.
 * 'true' if the tollge is being enabled and 'false' if it is being switched off.
 *
 * @param label    the label of the toggle (plain text or html)
 * @param callback the function to be called on change of the toggle's state
 * @param state    (optional) the initial state of the toggle, false by default
 *
 * @return HTML code for the newly created toggle element
 */ 
function createToggle(label, callback, state) {
    if(!state) state = false;
    if(!callback) callback = 0;
    retval = "";
    retval += '<span class="toggle' + (state ? 'On' : 'Off') + '" title="'+ label +'" ';
    retval += ' onClick="javascript:toggle(this, '+ callback +')">'+ label +"</span>";
    return retval;
}

/**
 * Creates HTML for vertical space inside the toolbar (EXPERIMENTAL).
 *
 * @param height the height of the vertical space in pixel
 *
 * @return HTML code for the vertical space
 */ 
function createVSpace(height) {
    return '<div style="width: 1px; height: '+ height +'px;"></div>';
}

/**
 * Creates HTML for horizontal space inside the toolbar (EXPERIMENTAL).
 *
 * @param height the height of the horizontal space in pixel
 *
 * @return HTML code for the horizontal space
 */
function createHSpace(width) {
    return '<span style="margin-left: '+ width +'px;"></span>';
}

/**
 * Creates HTML for a button element which opens a dialog to load a local file.
 * The label of the button can be either plain text, i.e. it will look like
 * a typical button, or it can be html code which allows for complex design.
 * A callback function is expected which will be executed when the load dialog
 * is closed with 'load' (not 'cancel'). This callback function will receive
 * the content of the file input of the load dialog as the only argument.
 *
 * @param id       a unique id for the load dialog (no spaces allowed!)
 * @param label    the label of the load button (plain text or html)
 * @param callback the function to be called on file load
 *
 * @return HTML code for the newly created button element
 */
function createLoadButton(id, label, callback) {
    // -- add dialog to the placeholder for all dialogs --
    dialogsDiv.innerHTML += createLoadDialog(id, label, callback);
    // -- define 
    dialogCB = "showDialog('"+ id +"')";
    retval = createButton(label, dialogCB);
    return retval;
}

/**
 * Creates HTML for a dialog element with which a local file can be loaded.
 * An ID has to be specified since the page can have more than one dialog. 
 * The text label is used to reflect the purpos of this dialog.
 * A callback function is expected which will be executed when the load button
 * is clicked. This callback function will receive the content of the file input
 * field as the only argument.
 * This function should be usally called by createLoadButton().
 *
 * @param id       a unique id for this dialog (no spaces allowed!)
 * @param label    the purpose of the dialog
 * @param callback the function to be called on load
 *
 * @return HTML code for the newly created button element
 */
function createLoadDialog(id, label, callback) {
    formName = id + 'Form';
    inputName = id + 'Input';
    wid = "widget_" + id;
    titlebar = "titlebar_" + id;
    finalCB = callback + "(document."+ formName +'.'+ inputName +".value)";
    retval = "<div class='dialog' style='top: 50px; left: 50px;' ";
    retval += "id='"+ id +"' title='"+ label +"'>";
    retval += "<div id='"+titlebar+"' class='titlebar'>"+ label +"</div>";
    retval += "<form name='" + formName +"'><br /><input type='file' name='"
    retval += inputName +"' />";
    retval += "<br /><input type='button' value='Cancel' ";
    retval += "onClick='javascript:hideDialog(\""+ id +"\")' />";
    retval += "<input type='button' value='load' onClick='javascript:"+ finalCB;
    retval += "; hideDialog(\""+ id +"\")' />";
    retval += "</form></div>";
    return retval;
}


//------------------------------------------------------------------------------
// private functions
//------------------------------------------------------------------------------

var x,y;
var z;                      // z index of top-most layer
var tx, ty;
var doDrag = false;
var activeObject = null;

document.onmousemove=mouseMove;
document.onmousedown=selectWidget;
document.onmouseup=unselectWidget;

/**
 * Set the currently active widget inactive.
 */
function unselectWidget() {
	if(doDrag) doDrag = false;
	activeObject = null;
}

/**
 * Sets the widget active that the user clicked on.
 */
function selectWidget(e) {	
	var target = getEventTarget(e);

	// only work on elements with ID
	if (!target.attributes['id']) return;
	
	// get the ID to decide what to do / what to work on
	var id = target.attributes['id'].value;
	
	//alert(id);
	
	// --- titlebar -> drag widget ---
	if(id.indexOf("titlebar_") != -1) {
			doDrag=true;
			x=e.clientX;
			y=e.clientY;
			activeObject = getParentWidget(id);
			//record widget location
			tx=parseInt(activeObject.style.left);
			ty=parseInt(activeObject.style.top);
			//change zIndex
			activeObject.style.zIndex=++z;
	}
}

/**
 * Get the Widget (entity) which contains the activated object
 * The naming convention is: "widget_123" contains "<childName>_123"
 */
function getParentWidget(childID) {
	var temp = childID.substring(childID.indexOf('_') + 1, childID.length);
	return document.getElementById(temp);
}

function mouseMove(e) {
  // only perform action if there is an active object
  if (activeObject == null) return;
  
  if (!isFirefox) {
    e = event;
  }
  
  if (doDrag) {
    activeObject.style.left = e.clientX + (tx - x) + 'px';
    activeObject.style.top  = e.clientY + (ty - y) + 'px';
  }
}



/**
 * Initializes all needed parts on page load.
 */
function init() {
//    alert("initialized! firefox: " + isFirefox);
   addDybar();
}

/**
 * Integgrates the actual DyBar into the using webpage
 */
function addDybar() {
    // -- create dybar top-level div --
    dybarDiv = document.createElement('div');
    // -- set style class for top-levl div --
    dybarDiv.className = 'dybarInline';
    // -- create tools for the dynamic toolbar --
    dybarDiv.innerHTML = createTools();

    // -- create shadow for overlay effects --
    shadow = document.createElement('span');
    shadow.id = 'shadow';
    shadow.style.zIndex = 10;
    z=10;
    shadow.innerHTML = '';

    // -- create container for dialogs --
    dialogsDiv = document.createElement('div');
    dialogsDiv.innerHTML = '';
    
    // -- insert dybar as first element in the body --
    body = document.getElementsByTagName('body')[0];
    body.insertBefore(dialogsDiv, body.childNodes[0]);
    body.insertBefore(dybarDiv, dialogsDiv);
    body.insertBefore(shadow, dybarDiv);
}

/**
 * Returns the target for the current event independent of the browser type.
 */
function getEventTarget(e) {
    var target = null;
    if (isFirefox)
		return e.target;
    return event.srcElement;
}

/**
 * Switches the state of a toggle and assigns the CSS classes 'ToggleOn' and
 * 'ToggleOff', respectively. If there is a callback function registered,
 * it will be called with 'true' or 'false', respectively. 
 */
function toggle(target, callback) {
    if (target.className == "toggleOn") {
        target.className = "toggleOff";
        if(callback) callback(false);
    } else if (target.className == "toggleOff") {
        target.className = "toggleOn";
        if(callback) callback(true);
    } else {
        alert("wrong state of toggle: " + target.className);
    }
}

/**
 * Perform the action related to a button
 */
function button(target, callback) {
    if(callback) callback();
}

/*
 * Add default tools here as HTML code
 */
function createTools() {
    retval = "";
    return retval;
}

/*
 * Show the dialog with the specified id
 *
 * @param id the id of the dialog to show
 */
function showDialog(id) {
    dialog = document.getElementById(id);
    shadow.style.visibility = 'visible';
    dialog.style.visibility = 'visible';
    dialog.style.zIndex = ++z;
}

/*
 * Hide the dialog with the specified id
 *
 * @param id the id of the dialog to hide
 */
function hideDialog(id) {
    dialog = document.getElementById(id);
    shadow.style.visibility = 'hidden';
    dialog.style.visibility = 'hidden';
}